package Entorno;

import java.util.concurrent.CopyOnWriteArraySet;

import Misil.Misil;
import Misil.MisilEnemigo;
import Baterias.Bateria;

/**
 * @brief Esta clase gestiona el paso del tiempo y la presencia de objetos en el
 *        espacio aereo (misiles). En principio esta clase estaria TERMINADA
 * @author Marcelo
 * @version 1.0
 * @created 17-jul-2012 09:08:20 p.m.
 */
public class Entorno {

	private CopyOnWriteArraySet<Misil> objetosDelEntorno;
	private int tiempo;
	private Bateria baterias[] = new Bateria[4];
	public Entorno m_Entorno;

	/**
	 * @brief Constructor predeterminado del entorno. La clase aplicacion se
	 *        encarga de llamarlo. En este metodo es donde se crean las
	 *        baterias.
	 */
	public Entorno() {
		objetosDelEntorno = new CopyOnWriteArraySet<Misil>();
		tiempo = 0;
		m_Entorno = this;
		generarBaterias();
	}

	/**
	 * @brief Funcion que agrega un nuevo misil aliado al entorno.
	 * @param nuevoAliado
	 */
	public void agregarMisilAliado(Misil nuevoAliado) {
		objetosDelEntorno.add(nuevoAliado);
	}

	/**
	 * @brief Funcion que genera un nuevo misil enemigo en una ubicacion
	 *        aleatoria con una velocidad tambien aleatoria. Tambien agrega ese
	 *        misil al entorno.
	 * @return misil enemigo generado
	 */
	public Misil generarMisilEnemigo() {
		MisilEnemigo misil = new MisilEnemigo();
		objetosDelEntorno.add(misil);

		double velocidad[] = new double[3];
		velocidad = misil.getComportamiento().getVectorDeVelocidad();
		velocidad[2] = -5;
		misil.getComportamiento().setVectorDeVelocidad(velocidad);

		return (Misil) misil;
	}

	/**
	 * @brief Funcion que agrega un nuevo misil enemigo con una ubicacion y una
	 *        velocidad determinadas. Tambien se agrega el nuevo misil al
	 *        entorno.
	 * @param posicion
	 *            inicial del nuevo misil enemigo.
	 * @param velocidad
	 *            del nuevo misil enemigo
	 * @return misil enemigo generado
	 */
	public Misil generarMisilEnemigo(double[] posicion, int velocidad) {
		MisilEnemigo misil = new MisilEnemigo(posicion, velocidad);
		objetosDelEntorno.add(misil);
		return (Misil) misil;
	}

	/**
	 * @brief Funcion que hace que las respectivas funciones de pasar el tiempo
	 *        de los demas objetos que dependen del tiempo sean ejecutadas.
	 */
	public void pasarTiempo() {
		tiempo++;
		for (int i = 0; i < 4; i++) {
			baterias[i].decrementarTiempo();
		}
		for (Misil misil : objetosDelEntorno) {
			misil.volar();
		}
		limpiarDestruidos();
		destruirSuperpuestos();
	}

	/**
	 * @brief getter del Setque contiene los misiles presentes en el entorno.
	 * @return conjunto de misiles aliados y enemigos presentes en el entorno.
	 */
	public CopyOnWriteArraySet<Misil> getObjetosDelEntorno() {
		return objetosDelEntorno;
	}

	/**
	 * @brief Funcion que devuelve el tiempo actual de la simulacion.
	 * @return tiempo actual de la simulacion
	 */
	public int getTime() {
		return tiempo;
	}

	/**
	 * @brief Funcion que crea a las 4 baterias con su ubicacion correspondiente
	 *        y les pasa una referencia a este entorno para que puedan pedirle
	 *        que agregue nuevos misiles aliados.
	 */
	private void generarBaterias() {
		// Generar Baterias
		for (int i = 0; i < 4; ++i) {
			double ubicacion[] = new double[3];
			if (i == 0) {
				ubicacion[0] = 0;
				ubicacion[1] = 10000;
				ubicacion[2] = 0;
			} else if (i == 1) {
				ubicacion[0] = 0;
				ubicacion[1] = -10000;
				ubicacion[2] = 0;
			} else if (i == 2) {
				ubicacion[0] = 10000;
				ubicacion[1] = 0;
				ubicacion[2] = 0;
			} else if (i == 3) {
				ubicacion[0] = -10000;
				ubicacion[1] = 0;
				ubicacion[2] = 0;
			}
			baterias[i] = new Bateria(m_Entorno, ubicacion, i);
		}
	}

	/**
	 * @brief Getter de las baterias. Es para poderselas pasar al monitor.
	 * @return arreglo de baterias.
	 */
	public Bateria[] getBaterias() {
		return baterias;
	}

	/**
	 * @brief Metodo que elimina de los objetos del entorno aquellos misiles que
	 *        han sido destruidos.
	 */
	private void limpiarDestruidos() {
		CopyOnWriteArraySet<Misil> nuevosObjetos = new CopyOnWriteArraySet<Misil>();
		for (Misil objeto : objetosDelEntorno) {
			if (!objeto.getDestruido()) {
				nuevosObjetos.add(objeto);
			}
		}
		objetosDelEntorno = nuevosObjetos;
	}

	/**
	 * @brief Metodo que checkea que no haya dos misiles ocupando la misma
	 *        posicion. Si los hay, ambos explotan
	 */
	private void destruirSuperpuestos() {
		for (Misil misil1 : objetosDelEntorno) {
			for (Misil misil2 : objetosDelEntorno) {
				if ((misil1 != misil2) && (!misil1.getDestruido())
						&& (!misil2.getDestruido())) {
					double p1[] = misil1.getPosition();
					double p2[] = misil2.getPosition();
					double dist[] = new double[3];
					dist[0] = p1[0] - p2[0];
					dist[1] = p1[1] - p2[1];
					dist[2] = p1[2] - p2[2];
					double modulo = Math.sqrt(dist[0] * dist[0] + dist[1]
							* dist[1] + dist[2] * dist[2]);
					if (modulo < 100) {
						misil1.explotar();
						misil2.explotar();
					}
				}
			}
		}
	}

}